import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.net.URL;

import com.jme.bounding.BoundingBox;
import com.jme.bounding.CollisionTree;
import com.jme.bounding.CollisionTreeManager;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.intersection.CollisionData;
import com.jme.intersection.CollisionResults;
import com.jme.intersection.TriangleCollisionResults;
import com.jme.math.FastMath;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.scene.CameraNode;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
import com.jme.scene.shape.Box;
import com.jme.system.DisplaySystem;
import com.jme.util.export.binary.BinaryImporter;
import com.jmex.game.state.CameraGameState;
import com.jmex.game.state.GameStateManager;
import com.jmex.model.converters.FormatConverter;
import com.jmex.model.converters.ObjToJme;


public class IngameState extends CameraGameState {

	private final float ROTATION_STEP = 90;
	private final float MOVE_STEP = 2f;
	private Node playerNode = new Node("player Node");
	private Spatial map;
	private CollisionResults results;
	private CollisionData oldData;
	private float playerSpeed = TestGameStateSystem.walkSpeed;
	private float rotationSpeed = TestGameStateSystem.rotateSpeed;    
	private Vector3f lastPosition;
	private Quaternion lastRotation;
	private float[] tmpAngles = new float[3];
	private Quaternion tmpRotationQuat = new Quaternion();
	private boolean isMoving;
	private boolean rforward,rbackward,rleft,rright;  // revert movement
	private float toRotate = 0;
	private float toMove = 0;

	private enum Direction {
		RIGHT,
		LEFT,
		FORWARD,
		BACKWARD;
	}
	Direction dir;    

	public IngameState(String name) {
		super(name);
		DisplaySystem display = DisplaySystem.getDisplaySystem();              

		CollisionTreeManager.getInstance().setTreeType(CollisionTree.Type.AABB);
		results = new TriangleCollisionResults();
		URL folder= IngameState.class.getClassLoader().getResource("data/");
		// import 3d object (dungeon)
		URL model = IngameState.class.getClassLoader().getResource("data/1210_export_c.obj");
		FormatConverter converter=new ObjToJme();
		converter.setProperty("mtllib", folder);
		converter.setProperty("texdir", folder);
		ByteArrayOutputStream BO=new ByteArrayOutputStream();

		try {
			converter.convert(model.openStream(), BO);
			map=(Spatial) BinaryImporter.getInstance().load(new ByteArrayInputStream(BO.toByteArray()));
			map.setLocalScale(0.1f);
			map.setModelBound(new BoundingBox());
			map.updateModelBound();
			
			// create a simple box to represent the player and test collision with dungeon
			Spatial player = new Box("box", new Vector3f(0,0,0), 1, 1 ,1);
			player.setLocalScale(0.1f);
			player.setModelBound(new BoundingBox());
			player.updateModelBound();

			System.out.printf("playnode=[%s]\n",playerNode.getLocalRotation().toString());
			playerNode.setLocalTranslation(3, 0.2f, 0);
			playerNode.attachChild(player);
			rootNode.attachChild(playerNode);
			rootNode.attachChild(map);
			rootNode.updateGeometricState(0, true);
			rootNode.updateRenderState();
		} catch (Exception e) {
			System.out.println("Damn exceptions! O_o yuurgh\n" + e);
			e.printStackTrace();
			System.exit(0);
		}
		KeyBindingManager.getKeyBindingManager().set("moveFwd", KeyInput.KEY_W);
		KeyBindingManager.getKeyBindingManager().set("moveBwd", KeyInput.KEY_S);
		KeyBindingManager.getKeyBindingManager().set("rotateLeft", KeyInput.KEY_A);
		KeyBindingManager.getKeyBindingManager().set("rotateRight", KeyInput.KEY_D);
		KeyBindingManager.getKeyBindingManager().set("strafeLeft", KeyInput.KEY_Q);
		KeyBindingManager.getKeyBindingManager().set("strafeRight", KeyInput.KEY_E);
		KeyBindingManager.getKeyBindingManager().set("exit", KeyInput.KEY_ESCAPE);

		CameraNode camNode = new CameraNode("", cam);
		playerNode.attachChild(camNode);
		camNode.setLocalTranslation(0, 0.7f, 0);
		lastPosition = new Vector3f();
		lastRotation = new Quaternion();

		lastPosition.set(playerNode.getLocalTranslation());
		cam.setFrustumPerspective(45.0f,
				(float) display.getWidth() /
				(float) display.getHeight(),
				0.002f, 350);
	}    

	public void onActivate() {
		DisplaySystem.getDisplaySystem().
		setTitle("Test Game State System");
		super.onActivate();
	}

	protected void stateUpdate(float tpf) {

		results.clear();            
		playerNode.findCollisions(rootNode, results);

		if (results.getNumber() <= 0) {
			System.out.printf("don't cross the dungeon limits dammit O_o\n");
			//System.exit(0);
			playerNode.getLocalTranslation().set(lastPosition);
		}

		oldData = results.getCollisionData(0);

		if (oldData.getTargetTris().size() == 0) {

			if (KeyBindingManager.getKeyBindingManager().isValidCommand("moveFwd", false)) {                
				if (isMoving) {
					rforward=true; //if we are already moving revert back to last position
				} else {
					lastPosition.set(playerNode.getLocalTranslation());
					isMoving = true;
					toMove = MOVE_STEP;
					dir = Direction.FORWARD;
				}
			}

			if (KeyBindingManager.getKeyBindingManager().isValidCommand("moveBwd", false)) {
				if (isMoving) {
					rbackward=true;
				} else {
					lastPosition.set(playerNode.getLocalTranslation());
					isMoving = true;
					toMove = MOVE_STEP;
					dir = Direction.BACKWARD;
				}
			}

			if (KeyBindingManager.getKeyBindingManager().isValidCommand("strafeLeft", true))
			{      
				Vector3f sideways = cam.getLeft();
				playerNode.getLocalTranslation().x += sideways.x * playerSpeed * tpf;
				playerNode.getLocalTranslation().z += sideways.z * playerSpeed * tpf;
			}
			if (KeyBindingManager.getKeyBindingManager().isValidCommand("strafeRight", true))
			{
				Vector3f sideways = cam.getLeft();
				playerNode.getLocalTranslation().x -= sideways.x * playerSpeed * tpf;
				playerNode.getLocalTranslation().z -= sideways.z * playerSpeed * tpf;
			}      

			// left rotation code
			if (KeyBindingManager.getKeyBindingManager().isValidCommand("rotateLeft", false))
			{
				if (isMoving) {
					rleft=true;
				} else {
					lastRotation.set(playerNode.getLocalRotation());// store rotation  before rotating                    
					isMoving = true;
					toRotate = ROTATION_STEP;
					dir = Direction.LEFT;
				}
			}

			// right rotation code
			if (KeyBindingManager.getKeyBindingManager().isValidCommand("rotateRight", false))  {
				if (isMoving) {
					rright=true;
				} else {
					lastRotation.set(playerNode.getLocalRotation());
					isMoving = true;
					toRotate = ROTATION_STEP;
					dir = Direction.RIGHT;
				}
			}

			// check if we should turn
			if (isMoving) {

				if (dir == Direction.RIGHT) {  // rotate 90deg right
					float value = 0;
					value = rotationSpeed*tpf;

					if (value >= toRotate) {
						value = toRotate;
						toRotate = 0;}
					else {
						toRotate -= value;
					}
					tmpAngles[0] = 0;                            // X
					tmpAngles[1] = -value * FastMath.DEG_TO_RAD;  // Y
					tmpAngles[2] = 0;                            // Z
					tmpRotationQuat.fromAngles(tmpAngles);
					playerNode.getLocalRotation().multLocal(tmpRotationQuat);
					oldData = results.getCollisionData(0);
					if (toRotate <= 0) {
						isMoving = false;
					}
					if (rleft){  // if LEFT pressed while rotating right, revert back.
						isMoving=false;
						toMove=0;
						playerNode.getLocalRotation().set(lastRotation);
						rleft=false;
					}
				}

				if (dir == Direction.LEFT) {
					float value = 0;
					value = rotationSpeed*tpf;
					//toRotate -= value;
					if(value >= toRotate) {
						value = toRotate;
						toRotate = 0;}
					else {
						toRotate -= value;
					}
					tmpAngles[0] = 0;                            // X
					tmpAngles[1] = value * FastMath.DEG_TO_RAD;  // Y
					tmpAngles[2] = 0;                            // Z
					tmpRotationQuat.fromAngles(tmpAngles);
					playerNode.getLocalRotation().multLocal(tmpRotationQuat);
					if (toRotate <= 0) {
						isMoving = false;
					}
					if (rright){
						isMoving=false;
						toMove=0;
						playerNode.getLocalRotation().set(lastRotation);
						rright=false;
					}
				}
				if (dir == Direction.FORWARD) {
					float value = 0;                  
					value = playerSpeed*tpf*-1;                    
					toMove += value;
					playerNode.getLocalTranslation().addLocal(cam.getDirection().normalize().multLocal(toMove*-value));
					if (toMove <= 0) {
						isMoving = false;
						System.out.printf("player=[%s]\n",cam.getDirection().normalize().toString());
						//System.out.printf("player=[%s]\n",playerNode.getLocalTranslation().toString());
					}
					if (rbackward){
						isMoving=false;
						toMove=0;
						playerNode.getLocalTranslation().set(lastPosition);
						rbackward=false;
					}                    
				}
				if (dir == Direction.BACKWARD){
					float value = 0;
					value = playerSpeed*tpf;
					toMove -= value;                    
					playerNode.getLocalTranslation().x -= cam.getDirection().x * toMove*value;
					playerNode.getLocalTranslation().z -= cam.getDirection().z * toMove*value;
					if (toMove <= 0) {
						isMoving = false;
					}
					if (rforward){
						isMoving=false;
						toMove=0;
						playerNode.getLocalTranslation().set(lastPosition);
						rforward=false;
					}
				}
			}

			if (KeyBindingManager.getKeyBindingManager().
					isValidCommand("exit", false)) {
				GameStateManager.getInstance().activateChildNamed("menu");
				GameStateManager.getInstance().detachChild("ingame");
			}
		}

		else
		{
			System.out.printf("*bump*\n");
			toMove=0;
			playerNode.getLocalTranslation().set(lastPosition);
		}        
	}    
}
